using System;
using System.Linq;
using System.Text.RegularExpressions;
using SQLRunnerLib.Services;
using SQLRunnerLib.Util;
using SQLServices.Log;
using SQLServices.Runners;
using SQLServices.Runners.Events;
using SQLRunnerLib.Project;
using SQLRunnerLib.Exceptions;

namespace SQLRunnerLib.Util
{
	/// <summary>
	/// Manages the execution of a SQL Runner project from the Command line
	/// </summary>
	public class LineController
	{
	    private long _scriptsExecuted = 0;
		private long _scriptsWithErrors = 0;
		private LineParser _args;
		private IRunner _runner;

		public int Start(LineParser args)
		{
            _args = args;

            // If the user wants to see help we don't need to validate any other arguments
            if (args.ShowHelp)
            {
                LineUsage.Print();
                return 0;
            }

            try
            {
                BuildEnvironment(args);
                args.Project.Validate();
                args.Environment.Validate();

                args.Environment.ConnectToDatabase();

                _runner = ServiceContainer.CreateRunner(args.Project, args.Environment, args.ConnMgrType);
                HookEvents(_runner);
                _runner.RunScripts();
                UnHookEvents(_runner);
                ServiceContainer.Instance.SQLConnection.Disconnect();
                ServiceContainer.Instance.Logger.LogMessage("Disconnected from the SQL Server", MsgLevel.Info);
                return (int)_scriptsWithErrors;
            }
            catch (ValidationException ex)
            {
                ServiceContainer.Instance.Logger.LogMessage(ex.Message, MsgLevel.Error);
                LineUsage.Print();
                return -1;
            }
            catch (Exception ex)
            {
                Regex expression = new Regex(@"(\[(\w*\s*)*\])+");
                string msg = expression.Replace(ex.Message, "");
                ServiceContainer.Instance.Logger.LogMessage(
                    String.Format("The application stopped as a result of:\n{0}", msg),
                    MsgLevel.Error);
                return -1;
            }
		}

        /// <summary>
        /// If an environment exists we update it with any connection values supplied
        /// If an environment does not exist we create a new environment from the supplied line arguments,
        /// this new environment is not saved anywhere it is just used by the Runner.  Doing this allows the runner to 
        /// always use an environment for its connection settings.
        /// </summary>
        /// <param name="args"></param>
        private void BuildEnvironment(LineParser args)
        {
            if (args.Environment == null)
            {
                args.Environment = new SQLEnvironment()
                {
                    Name = "CommandLine",
                    DatabaseName = args.DatabaseName,
                    ServerName = args.ServerName,
                    Trusted = args.TrustedConnection ?? false,
                    Username = args.UserName,
                    Password = args.Password
                };
            }
            else
            {
                // replace any values with those supplied

                if (!string.IsNullOrEmpty(args.DatabaseName))
                    args.Environment.DatabaseName = args.DatabaseName;
                if (!string.IsNullOrEmpty(args.ServerName))
                    args.Environment.ServerName = args.ServerName;
                if (!string.IsNullOrEmpty(args.UserName))
                {
                    args.Environment.Username = args.UserName;
                    // There is no way for a user to override the Trusted setting so base it on whether a username was supplied.
                    args.Environment.Trusted = false;
                }
                if (!string.IsNullOrEmpty(args.Password))
                    args.Environment.Password = args.Password;
                if (args.TrustedConnection.HasValue)
                    args.Environment.Trusted = args.TrustedConnection.Value;
            }
        }

		private void HookEvents(IRunner runner)
		{
			runner.ProgressMsgCreated += OnProgressMsgCreated;
			runner.ScriptExecError += OnScriptExecError;
			runner.ScriptExecuted += OnScriptExecuted;
			runner.ExecutionFinished += OnExecutionFinished;
		}

		private void UnHookEvents(IRunner runner)
		{
			runner.ProgressMsgCreated -= OnProgressMsgCreated;
			runner.ScriptExecError -= OnScriptExecError;
			runner.ScriptExecuted -= OnScriptExecuted;
			runner.ExecutionFinished -= OnExecutionFinished;
		}

		private void OnProgressMsgCreated(object sender, ProgressMsgCreatedEventArgs e)
		{
            ServiceContainer.Instance.Logger.LogMessage(e.Message, MsgLevel.Info);
		}

		private void OnScriptExecError(object sender, ScriptExecutionErrorEventArgs e)
		{
            ServiceContainer.Instance.Logger.LogMessage(
				String.Format("Error executing [{0}]: {1}", e.FileName, e.ErrorMsg),
				MsgLevel.Error);
			_scriptsExecuted++;
			_scriptsWithErrors++;
			if ( ( _args.ErrorLevel & ErrorIndicator.StopExecution ) == ErrorIndicator.StopExecution ) {
				_runner.CancelExecution();
			}
		}

		private void OnScriptExecuted(object sender, ScriptExecutedEventArgs e)
		{
            ServiceContainer.Instance.Logger.LogMessage(
				String.Format("Runner - script {0} was executed", e.FileName));
			_scriptsExecuted++;
		}

		private void OnExecutionFinished(object sender, ExecutionFinishedEventArgs e)
		{
			if ( _scriptsExecuted > 0 )
                ServiceContainer.Instance.Logger.LogMessage(
					String.Format("{0} scrips was/were executed", _scriptsExecuted),
					MsgLevel.Warn);
			if ( _scriptsWithErrors > 0 )
                ServiceContainer.Instance.Logger.LogMessage(
					String.Format("{0} scrips was/were executed with errors", _scriptsWithErrors),
					MsgLevel.Warn);
            ServiceContainer.Instance.Logger.LogMessage("Application finishing", MsgLevel.Info);
		}
	}

}
